﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
using NLog;
using SQLPXBase;

namespace GianosNET.Common.Classes
{
    public abstract class RemotedClass : IServerThreadable
    {
        public enum RemotedClassStatusEnum
        {
            None,
            Started,
            Stopped,
            Error
        }

        public enum LogTypeEnum
        {
            Info,
            Debug,
            Warning,
            Error,
            Fatal
        }

        private ServerInfo info;

        public Guid MainLogId { get; private set; }
        public Dictionary<Guid, string> logQueueId { get; private set; }

        protected Logger logger = null;
        protected ProxyManager<sqlStatus> statusProxyMgr = null;

        public string Gestore { get; protected set; }

        #region Costruttori

        public RemotedClass(bool enableAutoStart, ServerInfo info)
            : this("", "", new Hashtable(), null, enableAutoStart, info)
        {
        }

        public RemotedClass(string svcName, string version, Hashtable config, ProxyManager<sqlStatus> statusProxyMgr,
                            bool enableAutoStart, ServerInfo info)
        {
            Gestore = "GestoreGenerico";
            UniqueIdentifier = Guid.NewGuid();

            VersionNumber = version;
            ServiceName = svcName;

            this.info = info;

            // Inizializza e assegna il log principale del servizio
            MainLogId = info.RegisterLog(100);
            logQueueId = new Dictionary<Guid, string> {{MainLogId, "Log"}};

            this.config = config;
            this.statusProxyMgr = statusProxyMgr;

            bJob = GetServerJob();
            if (enableAutoStart && config.ContainsKey("autoStart") && config["autoStart"] as string == "true")
                Start();
        }

        #endregion

        private BackgroundWorker bJob;
        protected Hashtable config = new Hashtable();
        protected string configArea = "";
        protected RemotedClassStatusEnum status = RemotedClassStatusEnum.None;

        #region G/S

        public BackgroundWorker Job
        {
            get { return bJob; }
        }

        public ServerInfo ServerInfo
        {
            get { return info; }
        }

        #endregion

        #region Controlli sul servizio

        public string VersionNumber { get; protected set; }
        public string ServiceName { get; protected set; }
        public string InstanceName { get; set; }

        public void Start()
        {
            if (bJob != null && !bJob.IsBusy)
                bJob.RunWorkerAsync();

            Status = RemotedClassStatusEnum.Started;
        }

        public virtual void Stop()
        {
            if (bJob != null)
                bJob.CancelAsync();

            Status = RemotedClassStatusEnum.Stopped;

        }

        public void Restart()
        {
            
        }

        public virtual RemotedClassStatusEnum Status
        {
            get { return status; }
            set
            {
                if (status == value)
                    return; 

                status = value;

                // Propaga l'evento ai client
                info.PublishServiceStatusChange(UniqueIdentifier, value);

                // Segnala l'evento via SQL
                if (statusProxyMgr != null)
                    switch (value)
                    {
                        case RemotedClassStatusEnum.Error:
                            statusProxyMgr.GetProxy().NotifyServiceStatus(ServiceName, "ERROR");
                            break;
                        case RemotedClassStatusEnum.Stopped:
                            statusProxyMgr.GetProxy().NotifyServiceStatus(ServiceName, "STOPPED");
                            break;
                        case RemotedClassStatusEnum.Started:
                            statusProxyMgr.GetProxy().NotifyServiceStatus(ServiceName, "OK");
                            break;
                    }
            }
        }

        #endregion

        public void SetLogger(Logger logger)
        {
            this.logger = logger;
        }
        public bool HasLogger
        {
            get { return logger != null; }
        }

        #region Implementation of IServerThreadable

        public abstract BackgroundWorker GetServerJob();

        #endregion

        public bool Ping()
        {
            return true;
        }

        private Guid assemblyUniqueIdentifier = Guid.Empty;
        public Guid AssemblyUniqueIdentifier{
            get
            {
                if (assemblyUniqueIdentifier == Guid.Empty)
                {
                    Assembly ass = Assembly.GetAssembly(this.GetType());
                    assemblyUniqueIdentifier = new Guid((ass.GetCustomAttributes(typeof (GuidAttribute), false)[0] as GuidAttribute).Value);
                }

                return assemblyUniqueIdentifier;
            }
        }

        public Guid UniqueIdentifier
        {
            get; private set;
        }

        #region Logger Helper

         protected void DoLog(LogTypeEnum logType, string msg)
         {
             DoLog(logType, msg, null);
         }
         protected void DoLog(LogTypeEnum logType, string msg, Exception ex)
        {
            if (ex == null)
            {
                switch (logType)
                {
                    case LogTypeEnum.Info:
                        logger.Info(msg);
                        break;
                    case LogTypeEnum.Debug:
                        logger.Debug(msg);
                        break;
                    case LogTypeEnum.Warning:
                        logger.Warn(msg);
                        break;
                    case LogTypeEnum.Error:
                        logger.Error(msg);
                        break;
                    case LogTypeEnum.Fatal:
                        logger.Fatal(msg);
                        break;
                }
            }
            else
            {
                switch (logType)
                {
                    case LogTypeEnum.Info:
                        logger.InfoException(msg, ex);
                        break;
                    case LogTypeEnum.Debug:
                        logger.DebugException(msg, ex);
                        break;
                    case LogTypeEnum.Warning:
                        logger.WarnException(msg, ex);
                        break;
                    case LogTypeEnum.Error:
                        logger.ErrorException(msg, ex);
                        break;
                    case LogTypeEnum.Fatal:
                        logger.FatalException(msg, ex);
                        break;
                }
            }
        }

        #endregion
    }
}